package com.jcx.ldzj.service.impl;

import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.extension.api.R;
import com.baomidou.mybatisplus.extension.enums.ApiErrorCode;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.jcx.ldzj.config.UserConfig;
import com.jcx.ldzj.dao.UserInfoMapper;
import com.jcx.ldzj.dto.UserInfoDTO;
import com.jcx.ldzj.emun.MyCode;
import com.jcx.ldzj.emun.SystemCode;
import com.jcx.ldzj.entity.DeptUser;
import com.jcx.ldzj.entity.LoginUser;
import com.jcx.ldzj.entity.UserInfo;
import com.jcx.ldzj.entity.UserRoleRelation;
import com.jcx.ldzj.service.IDeptUserService;
import com.jcx.ldzj.service.IUserInfoService;
import com.jcx.ldzj.service.IUserRoleRelationService;
import com.jcx.ldzj.utile.AESUtile;
import com.jcx.ldzj.utile.RedisUtil;
import com.jcx.ldzj.utile.SnowFlakeUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.*;
import org.apache.shiro.crypto.hash.SimpleHash;
import org.apache.shiro.subject.Subject;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.servlet.http.HttpSession;
import java.util.List;
import java.util.stream.Collectors;

/**
 * <p>
 * 服务实现类
 * </p>
 *
 * @author Ð¡Ìì
 * @since 2021-01-20
 */
@Slf4j
@Service
@Transactional
public class UserInfoServiceImpl extends ServiceImpl<UserInfoMapper, UserInfo> implements IUserInfoService {
    @Autowired
    private IDeptUserService iDeptUserService;

    @Autowired
    private IUserRoleRelationService iUserRoleRelationService;
    @Autowired
    private RedisUtil redisclient;
    @Autowired
    private SnowFlakeUtil snowFlakeUtil;
    @Autowired
    private UserConfig userConfig;

    @Override
    public UserInfo getUserById(String id) {
        UserInfoDTO userInfoDto = new UserInfoDTO();
        UserInfo userInfo = this.baseMapper.selectById(id);
        BeanUtils.copyProperties(userInfo,userInfoDto);
        //组建用户与角色关系的
        List<UserRoleRelation> role = iUserRoleRelationService.getByuserId(id);
        List<String> roleIds = role.stream().map(UserRoleRelation::getRoleId).collect(Collectors.toList());
        userInfoDto.setRoleIds(roleIds);
        //组件用户与部门之间的关系数据
        DeptUser deptUser = new DeptUser();
        deptUser.setUserId(id);
        List<DeptUser> listByAll = iDeptUserService.getListByAll(deptUser);
        List<String> depIds = listByAll.stream().map(DeptUser::getDepId).collect(Collectors.toList());
        userInfoDto.setDepIds(depIds);
        return userInfoDto;
    }
    @Override
    public R findUser(UserInfoDTO userInfo){
        List<UserInfo> userInfos = this.baseMapper.apassLogin(userInfo);
        for (int i=0;userInfos!=null&&i<userInfos.size();i++){
            if (userInfos.get(i).getPhoneNumber().equals(userInfo.getPhoneNumber())) {
                return R.failed(MyCode.该用户电话号码已经存在_请重新输入);
            }
            return R.failed(MyCode.该用户名已经存在_请重新命名);
        }
        return R.ok("成功");
    }
    /**
     * @param userInfo
     * @return
     * 增加用户信息
     */
    @Override
    @CacheEvict(value = "user", allEntries = true, beforeInvocation = true)
    public R addUser(UserInfoDTO userInfo) {
            SimpleHash encode = AESUtile.MD5Utile(SystemCode.ORIGINAL_PASSWORD);
            userInfo.setPassWord(encode.toString());
            userInfo.setId(snowFlakeUtil.getIdString());
            //对用户所属部门关系进行修改
            iDeptUserService.saveUserToDept(userInfo);
            //对用户所属角色关系进行修改
            iUserRoleRelationService.saveUserAndRoles(userInfo.getRoleIds(),userInfo.getId(),null);
            redisclient.hPut(SystemCode.USER_MAP,userInfo.getUserName(),JSONObject.toJSONString(userInfo));
            userConfig.setRoles(userInfo);
            return R.ok(this.save(userInfo));
    }

    @Override
        public R updateUser(UserInfoDTO userInfo) {
        //对用户所属部门关系进行修改
         iDeptUserService.saveUserToDept(userInfo);
        //对用户所属角色关系进行修改
        iUserRoleRelationService.saveUserAndRoles(userInfo.getRoleIds(),userInfo.getId(),null);
        userConfig.setRoles(userInfo);

        redisclient.hPut(SystemCode.USER_MAP,userInfo.getUserName(),JSONObject.toJSONString(userInfo));
        return R.ok(this.updateById(userInfo));
    }

    @Override
    public R resetPassword(String userName) {

        String userMap = (String)redisclient.hGet(SystemCode.USER_MAP, userName);
        UserInfo user = JSONObject.parseObject(userMap,UserInfo.class);
        //重置密码为6个0，对密码进行加密
        SimpleHash simpleHash = AESUtile.MD5Utile(SystemCode.ORIGINAL_PASSWORD);
        //将加密的新密码存入数据库
        user.setPassWord(simpleHash.toString());
        //将最近修改的用户数据进行替换
        redisclient.hPut(SystemCode.USER_MAP,user.getUserName(),JSONObject.toJSONString(user));
        return R.ok(this.updateById(user));
    }

    /**
     * 修改或重置密码
     *
     * @param userName
     * @param emailCode 老密码
     * @param newPassword  新密码
     * @return
     */
    @Override
    public R updatePassword(HttpSession session, String userName, String emailCode, String newPassword) {
        String userMap = (String)redisclient.hGet(SystemCode.USER_MAP, userName);
        UserInfo user = JSONObject.parseObject(userMap,UserInfo.class);
        Object result=session.getAttribute("emailCode");
        R r = this.verificationCode(emailCode, result);
        if(r.getCode()!=0){
            return r;
        }
        SimpleHash simpleHash = AESUtile.MD5Utile(newPassword);
        //将加密的新密码存入数据库
        user.setPassWord(simpleHash.toString());
        //将最近修改的用户数据进行替换
        redisclient.hPut(SystemCode.USER_MAP,user.getUserName(),JSONObject.toJSONString(user));
        return R.ok(this.updateById(user));
    }

    @Override
    public R updatePwdOld(String oldPassword, String newPassword) {
        R<UserInfo> r = this.getUser();
        if(!r.ok()){
            return r;
        }
        UserInfo user = r.getData();
        SimpleHash simpleHash = AESUtile.MD5Utile(oldPassword);
        if(!simpleHash.toString().equals(user.getPassWord())){
            return R.failed(MyCode.旧密码错误_修改密码失败_请重新输入);
        }
        simpleHash = AESUtile.MD5Utile(newPassword);
        //将加密的新密码存入数据库
        user.setPassWord(simpleHash.toString());
        //将最近修改的用户数据进行替换
        redisclient.hPut(SystemCode.USER_MAP,user.getUserName(),JSONObject.toJSONString(user));
        return R.ok(this.updateById(user));
    }

    /**
     * 登录接口
     *
     * @param userName
     * @param passWord
     * @return
     */
    @Override
    public R<String> apassLogin(String userName, String passWord) {
        Subject curentUser = SecurityUtils.getSubject();
        UsernamePasswordToken token = new UsernamePasswordToken(userName, passWord);
        try {
            curentUser.login(token);
            curentUser.getSession().setAttribute("currentUser", curentUser.getPrincipal());
            //设置登录的session过期时间
            curentUser.getSession().setTimeout(600000);
        } catch (UnknownAccountException e) {
            log.error(MyCode.账号不存在_请重新输入.getMsg(), e);
            return R.failed(MyCode.账号不存在_请重新输入);
        } catch (IncorrectCredentialsException e) {
            log.error(MyCode.密码错误_请重新输入.getMsg(), e);
            return R.failed(MyCode.密码错误_请重新输入);
        } catch (LockedAccountException e) {
            log.error(MyCode.用户被锁定_请重新登录.getMsg(), e);
            return R.failed(MyCode.用户被锁定_请重新登录);
        } catch (AuthenticationException e) {
            log.error(MyCode.登录失败.getMsg(), e);
            return R.failed(MyCode.登录失败);
        }

        return R.ok(userName);
    }
    @Override
    public boolean isAdmin(String userName) {
        String userMap = (String)redisclient.hGet(SystemCode.USER_MAP, userName);
        UserInfo user = JSONObject.parseObject(userMap,UserInfo.class);
        if(user==null){
            return false;
        }
        //对admin用户不做任何权限查询校验，所以这里重写shiro权限校验方法
        if(user.isAdmin()){
            return true;
        }else{
            return false;
        }
    }


    @Override
    public R verificationCode(String verificationCode,Object result){
        if(result==null){
            return R.failed(MyCode.验证码已经过期_请重新加载);
        }
        //如果为字母验证码或者,这个是不区分大小写的
        if(result instanceof String){
            if(verificationCode.toUpperCase().equals(result.toString().toUpperCase())){
                return R.ok("成功");
            }
        //如果为数字验证码
        }else{
            if(result.toString().equals(verificationCode)){
                return R.ok("成功");
            }
        }
        return R.failed(MyCode.验证码输入错误_请重新输入);
    }

    @Override
    public R<UserInfo> getUser() {
        LoginUser login = (LoginUser) SecurityUtils.getSubject().getPrincipal();
        if(login==null){
            return R.failed(MyCode.用户未登录_请确认登录后再试);
        }
        String userMap = (String)redisclient.hGet(SystemCode.USER_MAP, login.getUserName());
        UserInfo user = JSONObject.parseObject(userMap,UserInfo.class);
        return R.ok(user);
    }

    @Override
    public R updateUserDept(String deptId,String deptName,String oldDeptName) {
        //如果部门名称没有更改的话，那么就不需要去该用户里面的信息了
        if(deptName.equals(oldDeptName)){
            return R.ok("成功");
        }
        UserInfo userInfo =new UserInfo();
        userInfo.setDepId(deptId);
        List<UserInfo> list = this.baseMapper.getUserLikeDeptName(userInfo);
        for (int i = 0; list!=null&&i < list.size(); i++) {
            UserInfo user = list.get(i);
            String replace = user.getDepName().replace(oldDeptName, deptName);
            user.setDepName(replace);
            this.updateById(user);
        }
        return R.ok("成功");
    }

    @Override
    public R deleteUser(String id) {
        UserInfo user = this.getUserById(id);
        redisclient.hDelete(SystemCode.USER_MAP,user.getUserName(),JSONObject.toJSONString(user));
        if(this.removeById(id)){
            return R.ok(ApiErrorCode.SUCCESS);
        }else{
            return R.failed(ApiErrorCode.FAILED);
        }
    }
}
